package textdiff {
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	
	
	public class TextPatcher {
		
		public function TextPatcher() {
			commands = new ArrayCollection();
		}
		
		public function edit(text:String, editScript:String): String {
			var editedText:String = "";
			getCommands(editScript);
			var editedLines: Array = executeCommands(text.split("\r"));
			for (var i:int; i<editedLines.length; i++) {
				editedText += editedLines[i];
				if (i<editedLines.length-1) editedText += "\r";
			}
			return editedText;
		}
		
		private function getCommands(editScript:String): void {
			commands.removeAll();
			var commandBlocks: Array = editScript.split("\r.\r");
			for (var i:int=0; i<commandBlocks.length; i++) {
				parseCommand(commandBlocks[i]);
			}
		}
		
		private function compareCommands(a: Object, b: Object, fields:Array=null): int {
			var ecA:EditCommand = a as EditCommand;
			var ecB:EditCommand = b as EditCommand;
			if (ecA.fromLineNum > ecB.fromLineNum) return 1;
			else if (ecA.fromLineNum == ecB.fromLineNum) return 0;
			else if (ecA.fromLineNum < ecB.fromLineNum) return -1;
			return 0;
		}
		
		private function executeCommands(textLines:Array): Array {
			var editedLines:Array = textLines;
			var prevLen:int = editedLines.length;
			var offset:int = 0;
			var cmd:EditCommand = null;
			var prevCmd:EditCommand = null;
			var sortedCommands:Array = commands.toArray().sort(compareCommands);
			
			for (var i:int=0; i<sortedCommands.length; i++) {
				cmd = sortedCommands[i] as EditCommand;
				if (i>0) {
					prevCmd = sortedCommands[i-1] as EditCommand;
					if (prevCmd.fromLineNum <= cmd.fromLineNum) {
						offset += editedLines.length-prevLen;
					}
				}
				prevLen = editedLines.length;
				editedLines = cmd.editText(editedLines, offset);
			}
			return editedLines;
		}
		
		private function parseCommand(cmdBlock: String): void {
			var cmd:Array = cmdBlock.split("\r");
			var lines:Array = cmd.slice(1);
			var cmdStr:String = cmd[0] as String;
			var cmdId:String = cmdStr.charAt(cmdStr.length-1);
			
			// get the line numbers for this command
			// first item is fromLineNumber, second item is thruLineNumber
			var lineNumbers:Array = cmdStr.substr(0,cmdStr.length-1).split(",");
			// set thruLineNumber to fromLineNumber in case no thruLineNumber is provided
			if (lineNumbers.length == 1) lineNumbers[1] = lineNumbers[0];
			
			var newCommand:EditCommand = null
			
			if (cmdId != null && cmdId.length == 1) {
				if (cmdId == "m") {
					newCommand = new MatchCommand();
					newCommand.command = "Move";
					newCommand.newLines = new LineBlock();
					newCommand.newLines.lines = lines;
					newCommand.newLines.fromLineNum = lineNumbers[0];
					newCommand.newLines.thruLineNum = lineNumbers[1];
					newCommand.oldLines = new LineBlock(null, lineNumbers[0]);
				}
				if (cmdId == "a") {
					newCommand = new AppendCommand();
					newCommand.newLines = new LineBlock();
					newCommand.newLines.lines = lines;
					newCommand.newLines.fromLineNum = lineNumbers[0];
					newCommand.oldLines = new LineBlock(null, lineNumbers[0]);
				}
				else if (cmdId == "c") {
					newCommand = new ChangeCommand();					
					newCommand.newLines = new LineBlock();
					newCommand.newLines.lines = lines;
					newCommand.oldLines = new LineBlock(null, lineNumbers[0], lineNumbers[1]);
				}
				else if (cmdId == "d") {
					newCommand = new DeleteCommand();					
					newCommand.oldLines = new LineBlock(null, lineNumbers[0], lineNumbers[1]);
				}
				else if (cmdId == "i") {
					newCommand = new InsertCommand();					
					newCommand.newLines = new LineBlock();
					newCommand.newLines.lines = lines;
					newCommand.oldLines = new LineBlock();
					newCommand.oldLines.fromLineNum = lineNumbers[0];
				}
			}
			if (newCommand != null) {
				commands.addItem(newCommand);
			}
		}
		
		private var commands:ArrayCollection;

	}
}